from decimal import Decimal
from django.db import transaction
from django.utils import timezone
from django_redis import get_redis_connection
from rest_framework import serializers
import logging

from goods.models import SKU
from orders.models import OrderInfo, OrderGoods


logger = logging.getLogger('django')


class CartSKUSerializer(serializers.ModelSerializer):
    """
    购物车商品数据序列化器
    """
    count = serializers.IntegerField(label='数量')

    class Meta:
        model = SKU
        fields = ('id', 'name', 'default_image_url', 'price', 'count')


class OrderSettlementSerializer(serializers.Serializer):
    """
    订单结果数据序列化器
    """
    freight = serializers.DecimalField(label='运费', max_digits=10, decimal_places=2)
    skus = CartSKUSerializer(many=True)


class SaveOrderSerializer(serializers.ModelSerializer):
    """
    下单数据序列化器
    """
    class Meta:
        model = OrderInfo
        fields = ('order_id', 'address', 'pay_method')
        read_only_fields = ('order_id',)
        extra_kwargs ={
            'address': {
                'write_only': True,
                'required': True,
            },
            'pay_method': {
                'write_only': True,
                'required': True,
            }
        }

    def create(self, validated_data):
        # 获取当前用户
        user = self.context["request"].user
        # 生成订单编号
        order_id = timezone.now().strftime('%Y%m%d%H%M%S') + ('%09d' % user.id)
        # 保存订单基本信息数据 OrderInfo
        address = validated_data['address']
        pay_method = validated_data['pay_method']
        # 从redis中获取购物车结算商品数据
        # 遍历结算商品：
             # 判断商品库存是否充足
             # 减少商品库存，增加商品销量
             # 保存订单商品数据
             # 在redis购物车中删除已计算商品数据
        with transaction.atomic():
            save_id = transaction.savepoint()
            try:
                # 从redis中获取购物车结算商品数据
                order = OrderInfo.objects.create(
                    order_id=order_id,
                    user=user,
                    address=address,
                    total_count=0,
                    total_amount=Decimal(0),
                    freight=Decimal(10),
                    pay_method=pay_method,
                    status=OrderInfo.ORDER_STATUS_ENUM['UNSEND'] if pay_method == OrderInfo.PAY_METHODS_ENUM['CASH']
                    else OrderInfo.ORDER_STATUS_ENUM['UNPAID']
                )
                redis_conn = get_redis_connection('cart')
                redis_cart = redis_conn.hgetall('cart_%s' % user.id)
                cart_selected = redis_conn.smembers('cart_selected_%s' % user.id)
                # 获取购物车所有勾选状态为True的商品信息
                # 将bytes类型转为int类型
                cart = {}
                for sku_id in cart_selected:
                    cart[int(sku_id)] = int(redis_cart[sku_id])
                for sku_id in cart:
                    while True:
                        # 获取当前sku_id 对应的商品信息
                        sku = SKU.objects.get(pk=sku_id)
                        sku_count = cart[sku.id]

                        # 判断库存
                        origin_stock = sku.stock
                        origin_sales = sku.sales
                        # 5.1 判断商品库存是否充足
                        if sku_count > origin_stock:
                            transaction.savepoint_rollback(save_id)
                            raise serializers.ValidationError('商品库存不足')
                        # 用于演示并发下单
                        import time
                        time.sleep(5)
                        new_stock = origin_stock - sku_count
                        new_sales = origin_sales + sku_count
                        # 使用乐观锁的步骤：
                        # 1.先记录在查询数据时， 保存原始数据值
                        # 2. 在更新的时候, 把原始数据值作为更新的条件
                        ret = SKU.objects.filter(id=sku.id, stock= origin_stock).update(stock=new_stock, sales=new_sales)
                        # 在乐观锁中并没有保存成功，那么就让用户重新判断样品库存
                        if ret == 0:
                            # 跳过本次循环
                            continue
                        # 如果ret的值不是0，则表示当前sku上的库存是充足的，也就是更新成功则执行后续代码
                        break
                        # 累计商品的spu 销量信息
                    sku.goods.sales += sku_count
                    sku.goods.save()

                    # 累计订单基本信息的数据
                    order.total_count += sku_count
                    order.total_amount += (sku.price * sku_count)

                    # 5.3 保存当前订单的商品数据
                    OrderGoods.objects.create(
                        order=order,
                        sku=sku,
                        count=sku_count,
                        price=sku.price,
                    )
                order.total_amount += order.freight
                order.save()
            except serializers.ValidationError:
                # 这里表示是序列化器，而校验错误抛出的异常是要给视图处理，返回给前端
                # 所以这里，我们直接抛出错误
                raise
            except Exception as e:
                logger.error(e)
                transaction.savepoint_rollback(save_id)
                raise
        transaction.savepoint_commit(save_id)

        pl = redis_conn.pipeline()
        pl.hdel('cart_%s' % user.id, *cart_selected)
        pl.srem('cart_selected_%s' % user.id, *cart_selected)
        pl.execute()

        return order
